///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include <SM.h>
//-----------------------------------------------------------------------------

SceneManager*
	SMScene = NULL;

Camera*
	SMViewCamera		= NULL;
Camera*
	SMLightCamera		= NULL;
Camera*
	SMCamera			= NULL;

GLuint
	SMfboId;
GLuint
	SMdepthTextureId;
GLuint
	SMshadowMapWidth;
GLuint
	SMshadowMapHeight;

void SMLoad()
{
	SMScene = MGL_NEW SceneManager();
	
	SMViewCamera = SMCamera = SMScene->GetCamera();
	SMCamera->moveAlongForward( 5 );
	
	SMLightCamera = MGL_NEW Camera();
	SMLightCamera->SetFov(45);
	SMLightCamera->SetNear(0.1f);
	SMLightCamera->SetFar(500.0f);

	SMScene->LoadScene( "SMScene.xml" );
	
	SMcreateFBO();
}

void SMInput()
{
	if( InputManager::GetInstance()->KeyPressed( '1' ) )
	{
		SMCamera = SMViewCamera;
	}
	if( InputManager::GetInstance()->KeyPressed( '2' ) )
	{
		SMCamera = SMLightCamera;
	}

	if( InputManager::GetInstance()->KeyPressed('q') )
	{
		SMCamera->moveAlongUp( -1 );
	}
	if( InputManager::GetInstance()->KeyReleased('q') )
	{
		SMCamera->moveAlongUp( 1 );
	}
	if( InputManager::GetInstance()->IsKeyDown('w') )
	{
		SMCamera->moveAlongForward( -0.1f );
	}
	if( InputManager::GetInstance()->IsKeyDown('s') )
	{
		SMCamera->moveAlongForward( 0.1f );
	}
	if( InputManager::GetInstance()->IsKeyDown('a') )
	{
		SMCamera->moveAlongRight( -0.1f );
	}
	if( InputManager::GetInstance()->IsKeyDown('d') )
	{
		SMCamera->moveAlongRight( 0.1f );
	}
	if( InputManager::GetInstance()->IsKeyDown('r') )
	{
		SMCamera->moveAlongUp( 0.1f );
	}
	if( InputManager::GetInstance()->IsKeyDown('f') )
	{
		SMCamera->moveAlongUp( -0.1f );
	}

	if( InputManager::GetInstance()->IsMouseButtonDown( 0 ) )
	{
		if( InputManager::GetInstance()->HasMouseMoved() )
		{
			if( InputManager::GetInstance()->IsMouseButtonDown( 0 ) )
			{
				SMCamera->rotateAroundRight( InputManager::GetInstance()->GetMouseOffsetY() / 10.f );
				SMCamera->rotateAroundY( InputManager::GetInstance()->GetMouseOffsetX() / 10.f );
			}

			if( InputManager::GetInstance()->IsMouseButtonDown( 2 ) )
			{
				SMCamera->moveAlongForward( InputManager::GetInstance()->GetMouseOffsetY() / 100.f );
				SMCamera->moveAlongRight( InputManager::GetInstance()->GetMouseOffsetX() / 100.f );
			}
		}
	}
}

void SMUpdate( f32 dt )
{
	SMScene->UpdateScene( dt );
}

void SMResolutionChanged()
{
	SMCamera->SetViewportSize( Application::GetInstance()->GetResolutionWidth(), Application::GetInstance()->GetResolutionHeight() );
}

void SMDraw()
{
	{
		MGL_PROFILE("shadow_draw");
		SMdrawFBO();
	}

	{
		MGL_PROFILE("shadow_draw");
		SMdrawNormal();
	}
}

void SMcreateFBO()
{
	SMshadowMapWidth = 1024;
	SMshadowMapHeight = 1024;
	
	//GLfloat borderColor[4] = {0,0,0,0};
	
	GLenum FBOstatus;
	
	// Try to use a texture depth component
	glGenTextures(1, &SMdepthTextureId);
	glBindTexture(GL_TEXTURE_2D, SMdepthTextureId);
	
	// GL_LINEAR does not make sense for depth texture. However, next tutorial shows usage of GL_LINEAR and PCF
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	
	// Remove artefact on the edges of the shadowmap
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
	
	//glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor );
	
	
	
	// No need to force GL_DEPTH_COMPONENT24, drivers usually give you the max precision if available 
	glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SMshadowMapWidth, SMshadowMapHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
	glBindTexture(GL_TEXTURE_2D, 0);
	
	// create a framebuffer object
	glGenFramebuffers(1, &SMfboId);
	glBindFramebuffer(GL_FRAMEBUFFER_EXT, SMfboId);
	
	// Instruct openGL that we won't bind a color texture with the currently binded FBO
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	
	// attach the texture to FBO depth attachment point
	glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_TEXTURE_2D, SMdepthTextureId, 0);
	
	// check FBO status
	FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
	if(FBOstatus != GL_FRAMEBUFFER_COMPLETE_EXT)
		printf("GL_FRAMEBUFFER_COMPLETE_EXT failed, CANNOT use FBO\n");
	
	// switch back to window-system-provided framebuffer
	glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);

	Texture* smt = TextureManager::GetInstance()->GetTextureOrCreate( "shadowMap" );
	smt->Load( SMdepthTextureId );
}

//-----------------------------------------------------------------------------

void SMdrawNormal()
{
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,0);

	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 
	glClearDepth(1.0f);
	//glClearColor(1, 1, 1, 0);
	glClearColor(0.5f, 0.5f, 0.5f, 0);
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glCullFace(GL_BACK);

	// adjust viewport and projection matrix to texture dimension
	glViewport(0, 0, Application::GetInstance()->GetResolutionWidth(), Application::GetInstance()->GetResolutionHeight());

	glMatrixMode(GL_PROJECTION);
	//glLoadIdentity();
	//gluPerspective(SMCamera->GetFov(), SMCamera->GetAspectRatio(), SMCamera->GetNear(), SMCamera->GetFar());
	glLoadMatrixf( (const GLfloat*)(SMCamera->getProjectionMatrix().m) );
	//glLoadMatrixf( (const GLfloat*)(SMCamera->getProjectionMatrix().GetGLMatrix().m) );
	glMatrixMode(GL_MODELVIEW);
	//glLoadIdentity();

	//gluLookAt( SMCamera->getPosition().x, SMCamera->getPosition().y, SMCamera->getPosition().z,
	//	SMCamera->getPosition().x + SMCamera->GetForward().x, SMCamera->getPosition().y + SMCamera->GetForward().y, SMCamera->getPosition().z + SMCamera->GetForward().z,
	//	SMCamera->GetUp().x, SMCamera->GetUp().y, SMCamera->GetUp().z );
	//f32 m[16];
	//glGetFloatv( GL_MODELVIEW_MATRIX, m );
	//glMultMatrixf( (const GLfloat*)(SMCamera->getView()) );
	glLoadMatrixf( (const GLfloat*)(SMCamera->getViewMatrix().m) );
	//f32 m1[16];
	//glGetFloatv( GL_MODELVIEW_MATRIX, m1 );

	//Texture* smt = TextureManager::GetInstance()->GetTexture( "shadowMap" );
	//if( smt )
	//{
	//	smt->bind( GL_TEXTURE7 );
	//}

	SettingsManager::GetInstance()->SetFlag(MGLS_RENDER_WITH_SHADOW, true);
	SMScene->SetCamera( SMCamera );
	SMScene->UpdateScene(0);

	glPushMatrix();

	SMScene->DrawScene();

	glPopMatrix();
	SettingsManager::GetInstance()->SetFlag(MGLS_RENDER_WITH_SHADOW, false);
}

//-----------------------------------------------------------------------------

void SMdrawFBO()
{
	Matrix4x4 projMat, mvMat;
	glBindFramebuffer(GL_FRAMEBUFFER_EXT,SMfboId);
	glViewport(0,0,SMshadowMapWidth,SMshadowMapHeight);
	glClearDepth(1.0f);
	glClear( GL_DEPTH_BUFFER_BIT);
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
	glCullFace(GL_FRONT);

	//setup view
	glMatrixMode(GL_PROJECTION);
	//glLoadIdentity();
	//gluPerspective( SMLightCamera->GetFov(), SMshadowMapWidth/SMshadowMapHeight, SMLightCamera->GetNear(), SMLightCamera->GetFar() );
	//SMLightCamera->SetViewportSize( SMshadowMapWidth, SMshadowMapHeight );
	//SMLightCamera->Update( 0.0f );
	SMScene->SetCamera( SMLightCamera );
	SMLightCamera->Update( 0.0f );
	//SMScene->UpdateScene(0);
	projMat = SMLightCamera->getProjectionMatrix();
	glLoadMatrixf( (const GLfloat*)(projMat.m) );
	glMatrixMode(GL_MODELVIEW);
	//glLoadIdentity();
	//gluLookAt( SMLightCamera->getPosition().x, SMLightCamera->getPosition().y, SMLightCamera->getPosition().z,
	//	SMLightCamera->getPosition().x + SMLightCamera->GetForward().x, SMLightCamera->getPosition().y + SMLightCamera->GetForward().y, SMLightCamera->getPosition().z + SMLightCamera->GetForward().z,
	//	SMLightCamera->GetUp().x, SMLightCamera->GetUp().y, SMLightCamera->GetUp().z );
	mvMat = SMLightCamera->getViewMatrix();
	glLoadMatrixf( (const GLfloat*)(mvMat.m) );

	SettingsManager::GetInstance()->SetFlag(MGLS_RENDER_DEPTH, true);

	//draw
	glPushMatrix();

	SMScene->DrawScene();

	glPopMatrix();
	SettingsManager::GetInstance()->SetFlag(MGLS_RENDER_DEPTH, false);

	Matrix4x4 shadowMatrix;
	shadowMatrix.SetIdentity();
	shadowMatrix.Scale( 0.5f, 0.5f, 0.5f );
	shadowMatrix.SetPosition( 0.5f, 0.5f, 0.5f );
	shadowMatrix = mvMat * projMat * shadowMatrix;

	SettingsManager::GetInstance()->SetShadowMatrix( shadowMatrix );
}

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
